package cn.hashq.netpoststation.util;

import cn.hashq.netpoststation.cache.ChannelCache;
import cn.hashq.netpoststation.entity.PortMap;
import cn.hashq.netpoststation.handler.map.EmptyDecoder;
import cn.hashq.netpoststation.handler.map.EmptyEncoder;
import cn.hashq.netpoststation.handler.map.PortMapDataHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.net.BindException;

@Slf4j
@Component
public class NettyUtil {
    private EventLoopGroup wg = new NioEventLoopGroup();

    public ChannelFuture openServerPort(int port, ChannelInitializer<SocketChannel> channels, GenericFutureListener listener) {
        try {
            EventLoopGroup bg = new NioEventLoopGroup(1);
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bg, wg);
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            bootstrap.localAddress(port);
            bootstrap.childHandler(channels);
            ChannelFuture future = bootstrap.bind().sync();
            future.addListener(listener);
            future.channel().closeFuture().addListener(new GenericFutureListener<Future<? super Void>>() {
                @Override
                public void operationComplete(Future<? super Void> future) throws Exception {
                    log.info("{}端口监听已停止", port);
                    bg.shutdownGracefully();
                }
            });
            return future;
        } catch (InterruptedException e) {
            log.error("打开端口{}失败,原因:{}", port, e.getMessage());
        } catch (Exception e) {
            if (e instanceof BindException) {
                throw new RuntimeException(port + "端口已被占用");
            }
        }
        return null;
    }

    public void startPortMap(PortMap portMap) {
        ChannelInitializer<SocketChannel> channelInitializer = new ChannelInitializer<SocketChannel>() {

            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline()
                        .addLast(new EmptyEncoder())
                        .addLast(new EmptyDecoder())
                        .addLast(new PortMapDataHandler(portMap));
            }
        };
        GenericFutureListener listener = new GenericFutureListener() {
            @Override
            public void operationComplete(Future future) throws Exception {
                if (future.isSuccess()) {
                    log.info("映射端口{}打开成功", portMap.getServerPort());
                } else {
                    log.info("映射端口{}打开失败,原因:{}", portMap.getServerPort(), future.cause().getMessage());
                }
            }
        };
        ChannelFuture channelFuture = openServerPort(portMap.getServerPort(), channelInitializer, listener);
        ChannelCache.getInstance().addChannel(channelFuture.channel());
    }
}
